/**********************************************************************//**
 * @file prc.c
 * @brief function summary
 *
 * Development Div. : PI&S SSE SYS1
 * Author : Yoichiro SATO\n
 * Copyright 2012 Sony Corporation. \n
 * ================================================================== */
/**********************************************************************/

/* --- include files ------------------------------------------------ */
#include <stdlib.h>
#include "prc.h"

/* --- macro definitions -------------------------------------------- */
/* --- structure/class definitions ---------------------------------- */
/* --- function prototypes ------------------------------------------ */
static int set_image_size(HW_IP_PRC *p_interface);
static int create_half_size_image(PRC_IMAGE *p_src, PRC_IMAGE *p_dst, int *p_work,
                                  int *p_src_y, int *p_src_cb, int *p_src_cr,
                                  int *p_dst_y, int *p_dst_cb, int *p_dst_cr);
static int create_half_size_element(int width, int height, int *p_src, int *p_dst, int *p_work);

/* --- static values ------------------------------------------------ */
/* --- auto values -------------------------------------------------- */


/* ------------------------------------------------------------------ *//**
 * @brief function summary
 * @param argument1 [in] explanation
 * @param argument2 [out] explanation
 * @retval 0 no error
 * @retval 1 error
 */
/* ------------------------------------------------------------------ */
int hw_ip_prc(HW_IP_PRC *p_interface)
{
    int x, y;
    
    int ercd = PRC_ERROR_NO;
    int src_width, src_height, src_bytewidth;
    
    int *p_work;
    int *p_work0_y;
    int *p_work0_cb;
    int *p_work0_cr;
    int *p_work1_y;
    int *p_work1_cb;
    int *p_work1_cr;

    // error check
    if (p_interface->image_a.width != PRC_IMAGE_A_WIDTH) {
        return PRC_ERROR_PARAM;
    }

    src_width     = p_interface->image_a.width;
    src_height    = p_interface->image_a.height;
    src_bytewidth = p_interface->image_a.bytewidth;
    
    p_work = (int*)malloc(sizeof(int)*src_width*src_height);
    
    p_work0_y  = (int*)malloc(sizeof(int)*src_width*src_height);
    p_work0_cb = (int*)malloc(sizeof(int)*src_width*src_height);
    p_work0_cr = (int*)malloc(sizeof(int)*src_width*src_height);
    
    p_work1_y  = (int*)malloc(sizeof(int)*src_width*src_height);
    p_work1_cb = (int*)malloc(sizeof(int)*src_width*src_height);
    p_work1_cr = (int*)malloc(sizeof(int)*src_width*src_height);
    
    set_image_size(p_interface);
    
    // get source image to local buffer
    for (y=0; y<src_height; y++) {
        for (x=0; x<src_width; x++) {
            
            p_work0_y[y*src_width + x] = (int)p_interface->image_a.p_data[y*src_bytewidth + 2*x + 1];
            
            if (x%2) {
                p_work0_cr[y*src_width + x - 1] = (int)p_interface->image_a.p_data[y*src_bytewidth + 2*x];
                p_work0_cr[y*src_width + x]     = (int)p_interface->image_a.p_data[y*src_bytewidth + 2*x];
            } else {
                p_work0_cb[y*src_width + x]     = (int)p_interface->image_a.p_data[y*src_bytewidth + 2*x];
                p_work0_cb[y*src_width + x + 1] = (int)p_interface->image_a.p_data[y*src_bytewidth + 2*x];
            }
        }
    }
    
    // A->B
    if (p_interface->image_b.p_data) {
        
        create_half_size_image(&p_interface->image_a, &p_interface->image_b, p_work,
                               p_work0_y, p_work0_cb, p_work0_cr,
                               p_work1_y, p_work1_cb, p_work1_cr);
    } else {
        ercd = PRC_ERROR_PARAM;
        goto DELETE_BUFFER;
    }
    
    // B->C
    if (p_interface->image_c.p_data) {
        
        create_half_size_image(&p_interface->image_b, &p_interface->image_c, p_work,
                               p_work1_y, p_work1_cb, p_work1_cr,
                               p_work0_y, p_work0_cb, p_work0_cr);
    } else {
        ercd = PRC_ERROR_PARAM;
        goto DELETE_BUFFER;
    }
    
    // C->D
    if (p_interface->image_d.p_data) {
        
        create_half_size_image(&p_interface->image_c, &p_interface->image_d, p_work,
                               p_work0_y, p_work0_cb, p_work0_cr,
                               p_work1_y, p_work1_cb, p_work1_cr);
    } else {
        ercd = PRC_ERROR_PARAM;
        goto DELETE_BUFFER;
    }
    
    // D->E
    if (p_interface->image_e.p_data) {

        create_half_size_image(&p_interface->image_d, &p_interface->image_e, p_work,
                               p_work1_y, p_work1_cb, p_work1_cr,
                               p_work0_y, p_work0_cb, p_work0_cr);
    } else {
        ercd = PRC_ERROR_PARAM;
        goto DELETE_BUFFER;
    }

 DELETE_BUFFER:
    
    free(p_work);
    
    free(p_work0_y);
    free(p_work0_cb);
    free(p_work0_cr);
    
    free(p_work1_y);
    free(p_work1_cb);
    free(p_work1_cr);
    
    return ercd;
}

/* ------------------------------------------------------------------ *//**
 * @brief function summary
 * @param argument1 [in] explanation
 * @param argument2 [out] explanation
 * @retval 0 no error
 * @retval 1 error
 */
/* ------------------------------------------------------------------ */
int create_half_size_image(PRC_IMAGE *p_src, PRC_IMAGE *p_dst, int *p_work,
                           int *p_src_y, int *p_src_cb, int *p_src_cr,
                           int *p_dst_y, int *p_dst_cb, int *p_dst_cr)
{
    int x, y;
    int ercd = PRC_ERROR_NO;
    
    //--------------------
    // Y,Cb,Cr
    //--------------------
    create_half_size_element(p_src->width, p_src->height, p_src_y, p_dst_y, p_work);
    create_half_size_element(p_src->width, p_src->height, p_src_cb, p_dst_cb, p_work);
    create_half_size_element(p_src->width, p_src->height, p_src_cr, p_dst_cr, p_work);
    
    for (y=0; y<p_dst->height; y++) {
        for (x=0; x<p_dst->width; x++) {
            
            p_dst->p_data[y*p_dst->bytewidth + 2*x + 1] = (unsigned char)p_dst_y[y*p_dst->width + x]; // Y
            
            if (x%2) {
                p_dst->p_data[y*p_dst->bytewidth + 2*x] = (unsigned char)p_dst_cr[y*p_dst->width + x]; // Cr
            } else {
                p_dst->p_data[y*p_dst->bytewidth + 2*x] = (unsigned char)p_dst_cb[y*p_dst->width + x]; // Cb
            }
        }
    }
    
    return ercd;
}

/* ------------------------------------------------------------------ *//**
 * @brief function summary
 * @param argument1 [in] explanation
 * @param argument2 [out] explanation
 * @retval 0 no error
 * @retval 1 error
 */
/* ------------------------------------------------------------------ */
int create_half_size_element(int width, int height, int *p_src, int *p_dst, int *p_work)
{
    int x, y;
    int i;
    int dst_width;

    dst_width = width >> 1;
    
    // horizontal operation
    i = 0;
    for (y=0; y<height; y++) {
        for (x=0; x<width; x+=2) {
            
            p_work[i] = (p_src[y*width + x] + p_src[y*width + x + 1] + 1) >> 1;
            i++;
        }
    }
    
    // vertical operation
    i = 0;
    if ((height%2) == 0) {

        for (y=0; y<height; y+=2) {
            for (x=0; x<dst_width; x++) {
                
                p_dst[i] = (p_work[y*dst_width + x] + p_work[(y + 1)*dst_width + x] + 1) >> 1;
                i++;
            }
        }
    } else {

        for (y=0; y<(height - 1); y+=2) {
            for (x=0; x<dst_width; x++) {
                
                p_dst[i] = (p_work[y*dst_width + x] + p_work[(y + 1)*dst_width + x] + 1) >> 1;
                i++;
            }
        }

        y = height - 1;
        for (x=0; x<dst_width; x++) {
            
            p_dst[i] = p_work[y*dst_width + x];
            i++;
        }
    }
    return 0;
}

/* ------------------------------------------------------------------ *//**
 * @brief function summary
 * @param argument1 [in] explanation
 * @param argument2 [out] explanation
 * @retval 0 no error
 * @retval 1 error
 */
/* ------------------------------------------------------------------ */
int set_image_size(HW_IP_PRC *p_interface)
{
    int ercd = PRC_ERROR_NO;
    int padding;
    
    p_interface->image_b.width  = (p_interface->image_a.width + 1) >> 1;
    p_interface->image_b.height = (p_interface->image_a.height + 1) >> 1;
    
    p_interface->image_c.width  = (p_interface->image_b.width + 1) >> 1;
    p_interface->image_c.height = (p_interface->image_b.height + 1) >> 1;
    
    p_interface->image_d.width  = (p_interface->image_c.width + 1) >> 1;
    p_interface->image_d.height = (p_interface->image_c.height + 1) >> 1;
    
    p_interface->image_e.width  = (p_interface->image_d.width + 1) >> 1;
    p_interface->image_e.height = (p_interface->image_d.height + 1) >> 1;
    
    p_interface->image_b.bytewidth = p_interface->image_b.width*2;
    p_interface->image_c.bytewidth = p_interface->image_c.width*2;
    p_interface->image_d.bytewidth = p_interface->image_d.width*2;
    p_interface->image_e.bytewidth = p_interface->image_e.width*2;
    
    if (p_interface->padding) {
        
        if ((padding = p_interface->image_b.bytewidth%(64*2)) > 0) {
            p_interface->image_b.bytewidth += (64*2 - padding);
        }
        if ((padding = p_interface->image_c.bytewidth%(64*2)) > 0) {
            p_interface->image_c.bytewidth += (64*2 - padding);
        }
        if ((padding = p_interface->image_d.bytewidth%(64*2)) > 0) {
            p_interface->image_d.bytewidth += (64*2 - padding);
        }
        if ((padding = p_interface->image_e.bytewidth%(64*2)) > 0) {
            p_interface->image_e.bytewidth += (64*2 - padding);
        }
    } 
    
    return ercd;
}
